package com.example.springboot_activiti_demo.activiti.service.impl;

import com.example.springboot_activiti_demo.activiti.Entity.response.RespEntity;
import com.example.springboot_activiti_demo.activiti.service.WorkFlowService;
import com.example.springboot_activiti_demo.activiti.util.SysConstant;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.image.ProcessDiagramGenerator;

import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @Author: Mingfang Zhu
 * @Description: 流程服务层实现类
 * @Date: Created in 13:44 2018/5/15
 */
@Service
public class WorkflowServiceImpl implements WorkFlowService {

    private org.slf4j.Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private ProcessEngineConfiguration processEngineConfiguration;

    @Autowired
    private ProcessEngine processEngine;

    /**
     * 流程图高亮
     * @param response
     * @param processKey
     * @param businessKey
     * @return
     */
    @Override
    public RespEntity traceFlowDiagram(HttpServletResponse response, String processKey, String businessKey) {
        RespEntity respEntity = new RespEntity();
        ProcessInstance processInstance  = runtimeService.createProcessInstanceQuery().processDefinitionKey(processKey).processInstanceBusinessKey(businessKey).singleResult();
        String processInstanceId  = processInstance.getId();
        //获取历史流程实例
//        HistoricProcessInstance historicProcessInstance =  historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        //获取流程图
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
        processEngineConfiguration = processEngine.getProcessEngineConfiguration();
        Context.setProcessEngineConfiguration((ProcessEngineConfigurationImpl) processEngineConfiguration);

        ProcessDiagramGenerator diagramGenerator = processEngineConfiguration.getProcessDiagramGenerator();
        ProcessDefinitionEntity definitionEntity = (ProcessDefinitionEntity)repositoryService.getProcessDefinition(processInstance.getProcessDefinitionId());

        List<HistoricActivityInstance> highLightedActivitList =  historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).list();
        //高亮环节id集合
        List<String> highLightedActivitis = new ArrayList<String>();
        //高亮线路id集合
        List<String> highLightedFlows = getHighLightedFlows(definitionEntity,highLightedActivitList);

        for(HistoricActivityInstance tempActivity : highLightedActivitList){
            String activityId = tempActivity.getActivityId();
            highLightedActivitis.add(activityId);
        }

        //中文显示的是口口口，设置字体就好了
        InputStream imageStream = diagramGenerator.generateDiagram(bpmnModel, "png", highLightedActivitis,highLightedFlows,"宋体","宋体","宋体",null,1.0);
        //单独返回流程图，不高亮显示
//        InputStream imageStream = diagramGenerator.generatePngDiagram(bpmnModel);
        // 输出资源内容到相应对象
        byte[] b = new byte[1024];
        int len;
        try {
            while ((len = imageStream.read(b, 0, 1024)) != -1) {
                response.getOutputStream().write(b, 0, len);
            }
            respEntity.setStatus(SysConstant.SUCCESS_STATUS);
            respEntity.setMsg("查看追踪流程图成功!");
        }catch (Exception e){
            respEntity.setStatus(SysConstant.FAIL_STATUS);
            respEntity.setMsg("出现位置异常："+e.toString());
        }

        return respEntity;
    }


    @Override
    public RespEntity withdraw(String processKey, String businessKey) {
        RespEntity respEntity = new RespEntity();
        ProcessInstance processInstance  = runtimeService.createProcessInstanceQuery().processDefinitionKey(processKey).processInstanceBusinessKey(businessKey).singleResult();
       return null;
    }

    /**
     * 查询历史审批列表
     * @param businessKey
     * @param assigneeId
     * @return
     */
    @Override
    public RespEntity queryHistoryTaskList(String businessKey,String assigneeId) {
        RespEntity respEntity = new RespEntity();
        try {
            List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
                    .processInstanceBusinessKey(businessKey).taskAssignee(assigneeId).list();
            respEntity.setStatus(0);
            respEntity.setMsg("查询流程历史节点成功！");
            respEntity.setData(list);
        }catch (Exception e){
            e.printStackTrace();
            respEntity.setStatus(0);
            respEntity.setMsg("查询流程历史节点异常："+e.toString());
        }
        return respEntity;
    }

    /**
     * 审批任务（下一节点为单实例userTask）
     * @param processKey  流程定义key
     * @param businessKey   业务主键
     * @param variables     流程变量
     * @return
     */
    @Override
    public RespEntity approveTask(String processKey, String businessKey,Map variables,String assignee) {
        RespEntity respEntity = new RespEntity();

        try {
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processDefinitionKey(processKey).processInstanceBusinessKey(businessKey).singleResult();
            Task task = taskService.createTaskQuery().processDefinitionKey(processKey).processInstanceId(processInstance.getId()).taskCandidateOrAssigned(assignee).singleResult();
            taskService.claim(task.getId(), assignee);
            taskService.complete(task.getId(), variables);
            respEntity.setStatus(SysConstant.SUCCESS_STATUS);
            respEntity.setMsg("审批任务成功！");
        }catch (Exception e){
            e.printStackTrace();
            respEntity.setStatus(SysConstant.FAIL_STATUS);
            respEntity.setMsg("审批任务失败！msg："+e.getMessage());
        }

        return respEntity;
    }


    /**
     * 待办任务接口
     * @param processKey  流程定义key
     * @param assignee    代理人id
     * @return
     */
    @Override
    public RespEntity queryToDoTask(String processKey, String assignee) {
        RespEntity respEntity = new RespEntity();
        List<String> keyList = new ArrayList<>();

        try {
            List<Task> todoList = taskService.createTaskQuery().processDefinitionKey(processKey).
                    taskCandidateOrAssigned(assignee).list();
            if (todoList.size()>0) {
                // 遍历任务 获取流程实例对象，再通过流程实例对象获取业务主键
                for (Task task : todoList) {
                    String processInstanceId = task.getProcessInstanceId();
                    ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processDefinitionKey(processKey).processInstanceId(processInstanceId).singleResult();
                    String businessKey = processInstance.getBusinessKey();
                    keyList.add(businessKey);
                }
            }
            respEntity.setStatus(SysConstant.SUCCESS_STATUS);
            respEntity.setMsg("查询待办任务请求成功！");
            respEntity.setData(keyList);
        }catch (Exception e){
            e.printStackTrace();
            respEntity.setStatus(SysConstant.FAIL_STATUS);
            respEntity.setMsg("请求失败！msg："+e.getMessage());
            respEntity.setData(keyList);
        }
        return respEntity;
    }

    /**
     * 启动流程接口
     * @param businessKey  业务主键
     * @param assigneeVars  申请人参数（以map形式传入）
     * @param nextAssigneeVars  下一审批人参数
     * @param processKey      流程定义key
     * @return
     */
    @Override
    public RespEntity startWorkFlow(String businessKey,Map assigneeVars, Map nextAssigneeVars, String processKey) {
        RespEntity respEntity = new RespEntity();
        try {
            //启动流程    （刚启动时流程停留在启动节点，而不是申请人节点）
            ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processKey,
                    businessKey, assigneeVars);
            //获取流程实例id
            String processInstanceId = processInstance.getId();
            //通过流程实例id获取任务对象
            Task task = taskService.createTaskQuery().processDefinitionKey(processKey).processInstanceId(processInstanceId).singleResult();
            //完成申请人节点任务，提交至下一节点
            taskService.complete(task.getId(), nextAssigneeVars);
            respEntity.setStatus(SysConstant.SUCCESS_STATUS);
            respEntity.setMsg("流程实例创建成功！实例ID："+processInstanceId);
            respEntity.setData(processInstanceId);
        }catch (ActivitiException e){
            if (e.getMessage().indexOf("no processes deployed with key") != -1) {
                logger.warn("没有部署流程!", e);
                respEntity.setStatus(SysConstant.FAIL_STATUS);
                respEntity.setMsg("没有部署流程！msg:"+e.getMessage());
            } else {
                logger.error("启动流程失败：", e);
                respEntity.setStatus(SysConstant.FAIL_STATUS);
                respEntity.setMsg("启动流程失败！msg:"+e.getMessage());
            }
        }catch (Exception e) {
            logger.error("启动流程失败：", e);
            respEntity.setStatus(SysConstant.FAIL_STATUS);
            respEntity.setMsg("启动流程失败！msg:"+e.getMessage());
        }
        return respEntity;
    }


    /**
     * 终止流程
     * @param processKey 流程定义key
     * @param businessKey  业务主键
     * @return
     */
    @Override
    public RespEntity terminateWorkFlow(String processKey, String businessKey) {
        RespEntity respEntity = new RespEntity();
        try {
            List<ProcessInstance> processInstanceList = runtimeService.createProcessInstanceQuery().processDefinitionKey(processKey).processInstanceBusinessKey(businessKey).list();
            if (processInstanceList != null && processInstanceList.size() > 0) {
                String processInstanceId = processInstanceList.get(0).getProcessInstanceId();
                System.out.println(processInstanceId);
                runtimeService.deleteProcessInstance(processInstanceId,"提前终止流程");
                respEntity.setStatus(SysConstant.SUCCESS_STATUS);
                respEntity.setMsg("流程实例终止成功！实例ID："+processInstanceId);
            }else {
                respEntity.setStatus(SysConstant.FAIL_STATUS);
                respEntity.setMsg("流程实例终止失败！未找到该实例");
            }
        }catch (Exception e){
            e.printStackTrace();
            respEntity.setStatus(SysConstant.FAIL_STATUS);
            respEntity.setMsg("流程实例终止失败！未知异常");
        }
        return respEntity;
    }


    /**
     * 获取需要高亮的线
     * @param processDefinitionEntity
     * @param historicActivityInstances
     * @return
     */
    private List<String> getHighLightedFlows(
            ProcessDefinitionEntity processDefinitionEntity,
            List<HistoricActivityInstance> historicActivityInstances) {
        List<String> highFlows = new ArrayList<String>();// 用以保存高亮的线flowId
        for (int i = 0; i < historicActivityInstances.size() - 1; i++) {// 对历史流程节点进行遍历
            ActivityImpl activityImpl = processDefinitionEntity
                    .findActivity(historicActivityInstances.get(i)
                            .getActivityId());// 得到节点定义的详细信息
            List<ActivityImpl> sameStartTimeNodes = new ArrayList<ActivityImpl>();// 用以保存后需开始时间相同的节点
            ActivityImpl sameActivityImpl1 = processDefinitionEntity
                    .findActivity(historicActivityInstances.get(i + 1)
                            .getActivityId());
            // 将后面第一个节点放在时间相同节点的集合里
            sameStartTimeNodes.add(sameActivityImpl1);
            for (int j = i + 1; j < historicActivityInstances.size() - 1; j++) {
                HistoricActivityInstance activityImpl1 = historicActivityInstances
                        .get(j);// 后续第一个节点
                HistoricActivityInstance activityImpl2 = historicActivityInstances
                        .get(j + 1);// 后续第二个节点
                if (activityImpl1.getStartTime().equals(
                        activityImpl2.getStartTime())) {
                    // 如果第一个节点和第二个节点开始时间相同保存
                    ActivityImpl sameActivityImpl2 = processDefinitionEntity
                            .findActivity(activityImpl2.getActivityId());
                    sameStartTimeNodes.add(sameActivityImpl2);
                } else {
                    // 有不相同跳出循环
                    break;
                }
            }
            List<PvmTransition> pvmTransitions = activityImpl
                    .getOutgoingTransitions();// 取出节点的所有出去的线
            for (PvmTransition pvmTransition : pvmTransitions) {
                // 对所有的线进行遍历
                ActivityImpl pvmActivityImpl = (ActivityImpl) pvmTransition
                        .getDestination();
                // 如果取出的线的目标节点存在时间相同的节点里，保存该线的id，进行高亮显示
                if (sameStartTimeNodes.contains(pvmActivityImpl)) {
                    highFlows.add(pvmTransition.getId());
                }
            }
        }
        return highFlows;
    }

    @Override
    public RespEntity releaseProcess(MultipartFile multipartFile) {
        RespEntity respEntity = new RespEntity();
        try{
            //第一步：上传文件
            //springmvc通过文件上传的参数解析器将页面提交的file赋值为形参
            //resource_bpmn和resource_png存储了上传的文件
            //第二步：调用activiti的service执行流程定义部署
            //部署bpmn文件和png文件
            //bpmn上传文件名
            String resourceName_bpmn = multipartFile.getOriginalFilename();
            InputStream inputStream_bpmn = multipartFile.getInputStream();

            //png上传文件名
//            String resourceName_png = resource_png.getOriginalFilename();
//            InputStream inputStream_png = resource_png.getInputStream();

            //部署对象
            Deployment deployment = repositoryService.createDeployment()
                    .addInputStream(resourceName_bpmn, inputStream_bpmn)//部署bpmn
//                    .addInputStream(resourceName_png, inputStream_png)//部署png
                    .deploy();
            //部署id
            System.out.println("部署id："+deployment.getId());
            System.out.println("部署时间：" + deployment.getDeploymentTime());
            respEntity.setStatus(SysConstant.SUCCESS_STATUS);
            respEntity.setMsg("流程文件发布成功！部署名称："+resourceName_bpmn+";部署id:"+deployment.getId());
//            String fileName=multipartFile.getOriginalFilename().substring(0,multipartFile.getOriginalFilename().lastIndexOf("."));
    //            File file = new File("testfilename");
    //            FileUtils.copyInputStreamToFile(multipartFile.getInputStream(), file);
    //            ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(file));
    //            repositoryService.createDeployment().name(fileName).addZipInputStream(zipInputStream).deploy();
//            System.out.println(file);
        }catch (Exception e){
            e.printStackTrace();
            logger.error(e.getMessage());
            respEntity.setStatus(SysConstant.FAIL_STATUS);
            respEntity.setMsg("流程文件部署失败！");
        }
//        try {
//            ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(file));
//            repositoryService.createDeployment().name(fileName).addZipInputStream(zipInputStream).deploy();
//            respEntity.setStatus(SysConstant.SUCCESS_STATUS);
//            respEntity.setMsg("流程文件发布成功！部署名称："+fileName);
//        } catch (FileNotFoundException e) {
//            logger.error(e.getMessage());
//            respEntity.setStatus(SysConstant.FAIL_STATUS);
//            respEntity.setMsg("流程文件部署失败！");
//        }
        return respEntity;
    }
}

